package com.engine4cloud.tdf.supervision.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.engine4cloud.tdf.common.core.constant.enums.AllAuditActionStatusEnum;
import com.engine4cloud.tdf.common.core.constant.enums.AuditBusinessTypeEnum;
import com.engine4cloud.tdf.common.core.constant.enums.CorFileTypeEnum;
import com.engine4cloud.tdf.common.core.constant.enums.OutEscalationStatusEnum;
import com.engine4cloud.tdf.common.core.exception.ValidateCodeException;
import com.engine4cloud.tdf.common.core.util.lambda.Actions;
import com.engine4cloud.tdf.common.data.web.query.Pages;
import com.engine4cloud.tdf.common.data.web.query.Queries;
import com.engine4cloud.tdf.common.data.web.response.PageInfo;
import com.engine4cloud.tdf.common.security.service.TdfUser;
import com.engine4cloud.tdf.common.security.util.SecurityUtils;
import com.engine4cloud.tdf.supervision.convert.SpOutApplicationConvert;
import com.engine4cloud.tdf.supervision.dto.SpOutApplicationVerifyDTO;
import com.engine4cloud.tdf.supervision.dto.create.SpAuditActionResultCreateDTO;
import com.engine4cloud.tdf.supervision.dto.create.SpOutApplicationCreateDTO;
import com.engine4cloud.tdf.supervision.dto.query.JzdxAreaQueryDTO;
import com.engine4cloud.tdf.supervision.dto.query.JzdxFileQueryDTO;
import com.engine4cloud.tdf.supervision.dto.query.SpOutApplicationQueryDTO;
import com.engine4cloud.tdf.supervision.dto.result.JzdxAreaResultDTO;
import com.engine4cloud.tdf.supervision.dto.result.JzdxResultDTO;
import com.engine4cloud.tdf.supervision.dto.result.OutApplicationSettingResultDTO;
import com.engine4cloud.tdf.supervision.dto.result.SpOutApplicationResultDTO;
import com.engine4cloud.tdf.supervision.dto.update.SpAllPreActionUpdateDTO;
import com.engine4cloud.tdf.supervision.dto.update.SpOutApplicationUpdateDTO;
import com.engine4cloud.tdf.supervision.entity.SpOutApplicationEntity;
import com.engine4cloud.tdf.supervision.mapper.SpOutApplicationMapper;
import com.engine4cloud.tdf.supervision.service.*;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 外出申请信息采集表
 *
 * @author wenjie
 * @date 2024-04-08 14:33:54
 */
@Service
public class SpOutApplicationServiceImpl extends ServiceImpl<SpOutApplicationMapper, SpOutApplicationEntity> implements SpOutApplicationService {
    @Resource
    private JzdxService jzdxService;
    @Resource
    private JzdxAreaService areaService;
    @Resource
    private OutApplicationSettingService outApplicationSettingService;
    @Resource
    private SpAuditResultService auditResultService;
    @Resource
    private JzdxFileService fileService;

    @Override
    public PageInfo<SpOutApplicationResultDTO> pageList(SpOutApplicationQueryDTO dto) {
        IPage<SpOutApplicationEntity> page = super.page(Pages.page(dto), wrapper(dto));
        return Pages.convert(page, SpOutApplicationConvert.INSTANCE::convert);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean create(SpOutApplicationCreateDTO dto) {
        JzdxResultDTO dx = Optional.ofNullable(jzdxService.getByCode(dto.getRybh()))
                .orElseThrow(() -> new ValidateCodeException("矫正对象数据不存在"));
        TdfUser user = SecurityUtils.getUser();

        OutApplicationSettingResultDTO defaultSetting = Optional.ofNullable(outApplicationSettingService.findDefaultSetting())
                .orElseThrow(() -> new ValidateCodeException("外出配置数据不存在，请联系管理员"));

        //累计请假天数
        BigDecimal escalation = this.duration(dto.getRybh(), dto.getKsqr().minusDays(defaultSetting.getRange()));
        //保存外出信息
        SpOutApplicationEntity entity = SpOutApplicationConvert.INSTANCE.create(dto, dx.getTenantId(), AllAuditActionStatusEnum.CREATE, escalation, defaultSetting.getRange());
        //设置外出目的地信息
        this.setArea(entity);
        super.save(entity);
        //保存文件信息
        this.saveInstrumentList(dto, entity, user);
        //设置审核信息
        return auditResultService.create(
                new SpAuditActionResultCreateDTO(
                        entity.getRybh(),
                        entity.getLcbh(),
                        "",
                        AuditBusinessTypeEnum.OUT_APPLICATION,
                        AllAuditActionStatusEnum.CREATE,
                        user.getUsername(),
                        entity.getTenantId(),
                        user.getDeptId()
                ));
    }

    @Override
    public Boolean update(SpOutApplicationUpdateDTO dto) {
        SpOutApplicationEntity old = Optional.ofNullable(super.getById(dto.getId()))
                .orElseThrow(() -> new ValidateCodeException("数据不存在"));

        OutApplicationSettingResultDTO defaultSetting = Optional.ofNullable(outApplicationSettingService.findDefaultSetting())
                .orElseThrow(() -> new ValidateCodeException("外出配置数据不存在，请联系管理员"));

        TdfUser user = SecurityUtils.getUser();
        //转换
        SpOutApplicationEntity entity = SpOutApplicationConvert.INSTANCE.update(dto, old.getDuration(), old.getWcts(), defaultSetting.getRange());
        //设置外出目的地信息
        this.setArea(entity);
        //更新
        super.updateById(entity);
        //保存文件信息
        this.updateInstrumentList(dto, old, user);
        return true;
    }

    private BigDecimal duration(String code, LocalDateTime startTime) {
        SpOutApplicationQueryDTO query = new SpOutApplicationQueryDTO();
        query.setRybh(code);
        query.setPreAction(AllAuditActionStatusEnum.CANCEL);
        query.setStartTime(startTime);
        List<SpOutApplicationEntity> existList = super.list(wrapperEscalation(query));
        if (CollectionUtils.isEmpty(existList)) {
            return new BigDecimal(0);
        }

        return existList.stream().map(SpOutApplicationEntity::getWcts).reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    @Override
    public SpOutApplicationResultDTO getById(Long id) {
        SpOutApplicationResultDTO result = Optional.ofNullable(SpOutApplicationConvert.INSTANCE.convert(super.getById(id)))
                .orElseThrow(() -> new ValidateCodeException("数据不存在"));
        //设置法律文书信息
        this.setInstrumentList(result);
        return result;
    }

    @Override
    public SpOutApplicationResultDTO getByBatch(String batch) {
        return SpOutApplicationConvert.INSTANCE.convert(super.getOne(wrapper(new SpOutApplicationQueryDTO(batch))));
    }

    @Override
    public Boolean close(String batch) {
        SpOutApplicationResultDTO outApplication = Optional.ofNullable(getByBatch(batch))
                .orElseThrow(() -> new ValidateCodeException("数据不存在"));

        SpOutApplicationEntity entity = SpOutApplicationConvert.INSTANCE.close(outApplication);
        entity.setXjsj(LocalDateTime.now());
        return super.updateById(entity);
    }

    @Override
    public List<SpOutApplicationResultDTO> findListByPreActions(SpOutApplicationVerifyDTO dto) {
        return super.list(wrapperVerify(dto)).stream().map(SpOutApplicationConvert.INSTANCE::convert).toList();
    }

    @Override
    public void changeAction(SpAllPreActionUpdateDTO dto) {
        SpOutApplicationEntity entity = Optional.ofNullable(this.getOne(wrapper(new SpOutApplicationQueryDTO(dto.getRybh(), dto.getLcbh()))))
                .orElseThrow(() -> new ValidateCodeException("数据不存在"));

        entity.setPreAction(dto.getAction());
        super.updateById(entity);
    }

    @Override
    public Boolean escalation(Long id) {
        SpOutApplicationEntity old = Optional.ofNullable(super.getById(id))
                .orElseThrow(() -> new ValidateCodeException("数据不存在"));
        old.setEscalation(OutEscalationStatusEnum.PROCESSED);
        return super.updateById(old);
    }


    private void setArea(SpOutApplicationEntity entity) {
        JzdxAreaResultDTO area = areaService.getArea(new JzdxAreaQueryDTO(entity.getWcmddszs(), entity.getWcmddszd(), entity.getWcmddszx(), Objects.isNull(entity.getWcmddxz()) ? null : Long.parseLong(entity.getWcmddxz())));
        entity.setWcmddszsmc(area.getProvince().getName());
        entity.setWcmddszdmc(area.getCity().getName());

        Actions.apply(Objects.nonNull(area.getCounty()), () -> entity.setWcmddszxmc(area.getCounty().getName()));
        Actions.apply(Objects.nonNull(area.getVillage()), () -> entity.setWcmddxzmc(area.getVillage().getName()));
    }

    public void saveInstrumentList(SpOutApplicationCreateDTO dto, SpOutApplicationEntity entity, TdfUser user) {
        fileService.batchSaveFile(new JzdxFileQueryDTO(entity.getRybh(), entity.getLcbh(), CorFileTypeEnum.CORRECTOR_OUT_APPLICATION), dto.getInstrumentList(), user);
    }

    private void updateInstrumentList(SpOutApplicationUpdateDTO dto, SpOutApplicationEntity entity, TdfUser user) {
        fileService.batchUpdateFile(new JzdxFileQueryDTO(entity.getRybh(), entity.getLcbh(), CorFileTypeEnum.CORRECTOR_OUT_APPLICATION), dto.getInstrumentList(), user);
    }

    private void setInstrumentList(SpOutApplicationResultDTO dto) {
        JzdxFileQueryDTO query = new JzdxFileQueryDTO(dto.getRybh(), dto.getLcbh(), CorFileTypeEnum.CORRECTOR_OUT_APPLICATION);
        dto.setInstrumentList(fileService.findList(query));
    }

    private LambdaQueryWrapper<SpOutApplicationEntity> wrapper(SpOutApplicationQueryDTO query) {
        LambdaQueryWrapper<SpOutApplicationEntity> wrapper = Queries.lambda();
        Queries.accept(query.getId(), v -> wrapper.eq(SpOutApplicationEntity::getId, v));
        Queries.accept(query.getRybh(), v -> wrapper.eq(SpOutApplicationEntity::getRybh, v));
        Queries.accept(query.getLcbh(), v -> wrapper.eq(SpOutApplicationEntity::getLcbh, v));
        Queries.accept(query.getPreAction(), v -> wrapper.eq(SpOutApplicationEntity::getPreAction, v));
        return wrapper;
    }

    private LambdaQueryWrapper<SpOutApplicationEntity> wrapperEscalation(SpOutApplicationQueryDTO query) {
        LambdaQueryWrapper<SpOutApplicationEntity> wrapper = Queries.lambda();
        Queries.accept(query.getRybh(), v -> wrapper.eq(SpOutApplicationEntity::getRybh, v));
        Queries.accept(query.getPreAction(), v -> wrapper.ne(SpOutApplicationEntity::getPreAction, v));
        Queries.accept(query.getStartTime(), v -> wrapper.gt(SpOutApplicationEntity::getKsqr, v));
        return wrapper;
    }

    private LambdaQueryWrapper<SpOutApplicationEntity> wrapperVerify(SpOutApplicationVerifyDTO query) {
        LambdaQueryWrapper<SpOutApplicationEntity> wrapper = Queries.lambda();
        Queries.accept(query.getRybh(), v -> wrapper.eq(SpOutApplicationEntity::getRybh, v));
        Queries.accept(query.getLocTime(), v -> wrapper.lt(SpOutApplicationEntity::getKsqr, v)
                .ge(SpOutApplicationEntity::getJsrq, v));
        Queries.accept(query.getEnumList(), v -> wrapper.in(SpOutApplicationEntity::getPreAction, v));
        return wrapper;
    }
}